﻿
using System;
using System.Diagnostics;
using System.Drawing;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace Glorysoft.EAP.EAPClient.Common
{
    public delegate void MouseClickEventHandler(IntPtr Handler);
    public delegate void KeyEventHandler(IntPtr handler);
    public delegate void MouseDownHandler(object sender, System.Windows.Forms.MouseEventArgs e);
    public delegate void MouseUpHandler(object sender, System.Windows.Forms.MouseEventArgs e);
    public delegate void KeyDownHandler(object sender, System.Windows.Forms.KeyEventArgs e);
    public delegate void MouseMoveHandler(object sender, System.Windows.Forms.MouseEventArgs e);
    public class Hook : IDisposable
    {
        protected bool m_disposed = false;
        protected bool _mouseStarted = false;
        protected bool _keyStarted = false;
        protected int _handleToHook;
        protected int _keyhandleToHook;
        protected const int WH_MOUSE_LL = 14;
        protected const int WM_RBUTTONUCLICK = 0x205;
        protected const int WM_RBUTTONDCLICK = 0x204;

        protected const int WM_MOUSEMOVE = 512;// 0x200;
        protected const int WM_LBUTTONDOWN = 513;
        protected const int WM_LBUTTONUP = 514;
        protected const int WM_RBUTTONDOWN = 516;
        protected const int WM_RBUTTONUP = 517;
        protected const int WM_MBUTTONDOWN = 519;

        //protected const int WM_SYSKEYDOWN = 0x0104;
        protected const int WM_KEYUP = 0x0101;
        protected const int WM_SYSKEYUP = 0x0105;

        public event MouseDownHandler MouseDownEvent;
        public event MouseUpHandler MouseUpEvent;
        public event KeyDownHandler KeyDownEvent;
        public event MouseClickEventHandler DoubleClick;
        public event MouseMoveHandler MouseMoveEvent;
        protected int _MousehookType = 0;
        protected int _KeyhookType = 0;
        public HookProc _MousehookCallback; //去掉static，两单元同时切换操作，会崩溃。monitorThreadId MouseCallbackProcedure会重新初始为默认值0X000000
        public HookProc _KeyhookCallback;
        private static int EventParam = 0;
        private static IntPtr StructParam = IntPtr.Zero;
        private static bool opened = false;
        private IntPtr monitorInstance;
        private int monitorThreadId;
        private KvmRect kvmRect;

        private DateTime lastKeyEventTime;
        private DateTime lastMouseEventTime;
        private DateTime keymouseEventTime;
        public Hook()
        {
            _MousehookType = (int)HookTypes.WH_MOUSE_LL;
            _KeyhookType = (int)HookTypes.WH_KEYBOARD_LL;
        }

        public DateTime LastKeyEventTime
        {
            get
            {
                return lastKeyEventTime;
            }
        }
        public DateTime LastMouseEventTime
        {
            get
            {
                return lastMouseEventTime;
            }
        }
        public DateTime KeyMouseEventTime
        {
            get
            {
                return keymouseEventTime;
            }
        }
        [HandleProcessCorruptedStateExceptions]
        private IntPtr getHookInstance(IntPtr instance)
        {
            IntPtr hMod = IntPtr.Zero;
            //if (instance == IntPtr.Zero)
            hMod = Win32API.GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);
            //else
            //    hMod = instance;
            return hMod;
        }

        private void updateKeyTime()
        {
            var time = DateTime.Now;
            this.lastKeyEventTime = time;
            this.keymouseEventTime = time;
        }
        private void updateMouseTime()
        {
            var time = DateTime.Now;
            this.lastKeyEventTime = time;
            this.keymouseEventTime = time;
        }
        [HandleProcessCorruptedStateExceptions]
        public bool MouseHookStart(IntPtr instance, int threadId)
        {
            try
            {
                if (!_mouseStarted)
                {

                    Win32API.GetWindowRect(instance, out kvmRect);
                    updateMouseTime();
                    monitorThreadId = threadId;
                    monitorInstance = instance;
                    IntPtr hMod = getHookInstance(instance);
                    _MousehookCallback = new HookProc(MouseCallbackProcedure);
                    _handleToHook = Win32API.SetWindowsHookEx(
                        _MousehookType,
                        _MousehookCallback,
                        hMod,
                       0);
                    if (0 == _handleToHook)
                    {
                        int errCode = Marshal.GetLastWin32Error();
                        return false;
                    }
                    updateMouseTime();
                    _mouseStarted = true;
                }
                return true;
            }
            catch (Exception ex)
            {
                // LogHelper.HostErrorInfo(string.Format("Hook MouseHookStart. handler--{0}", monitorInstance), ex);
                return false;
            }
        }
        [HandleProcessCorruptedStateExceptions]
        public bool KeyHookStart(IntPtr instance, int threadId)
        {
            try
            {
                if (!_keyStarted)
                {
                    updateKeyTime();
                    IntPtr hMod = getHookInstance(instance);
                    monitorThreadId = threadId;
                    monitorInstance = instance;
                    _KeyhookCallback = new HookProc(KeyCallbackProcedure);
                    _keyhandleToHook = Win32API.SetWindowsHookEx(
                        _KeyhookType,
                        _KeyhookCallback,
                        hMod,
                        0);
                    if (0 == _keyhandleToHook)
                    {
                        int errCode = Marshal.GetLastWin32Error();
                        return false;
                    }
                    updateKeyTime();
                    _keyStarted = true;
                }
                return true;
            }
            catch (Exception ex)
            {
                // LogHelper.HostErrorInfo(string.Format("Hook KeyHookStart. handler--{0}", monitorInstance), ex);
                return false;
            }
        }
        [HandleProcessCorruptedStateExceptions]
        public int KeyCallbackProcedure(int nCode, int wparam, IntPtr lparam)
        {
            int kevent = (int)wparam;
            if (kevent == WM_KEYUP || kevent == WM_SYSKEYUP)
            {
                EventParam = wparam;
                StructParam = lparam;
                KeyMSG keyboardHookStruct = (KeyMSG)Marshal.PtrToStructure(lparam, typeof(KeyMSG));//获取钩子的相关信息
                KeyEventArgs keyEventArgs = new KeyEventArgs((Keys)(keyboardHookStruct.vkCode));//获取KeyEventArgs事件的相磁信息
                opened = true;
                try
                {
                    if (kevent == WM_KEYUP)
                    {
                        if (this.KeyDownEvent != null)
                        {
                            KeyDownEvent(this, keyEventArgs);
                        }

                    }
                    // updateKeyTime();
                }
                catch (Exception)
                {
                }
            }
            try
            {
                return Win32API.CallNextHookEx(_keyhandleToHook, nCode, wparam, lparam);
            }
            catch (Exception ex)
            {
                // LogHelper.HostErrorInfo(string.Format("Hook KeyCallbackProcedure. handler--{0}", monitorInstance), ex);
                return 0;
            }

        }
        [HandleProcessCorruptedStateExceptions]
        public int MouseCallbackProcedure(int nCode, int wparam, IntPtr lparam)
        {
            int mevent = (int)wparam;
            if (mevent == WM_LBUTTONUP || mevent == WM_RBUTTONUP || mevent == WM_MBUTTONDOWN || mevent == WM_LBUTTONDOWN || mevent==WM_MOUSEMOVE)
            {
                EventParam = wparam;
                StructParam = lparam;
                opened = true;
                try
                {
                    //MouseEvents mEvent = (MouseEvents)EventParam;
                    //if (mEvent == MouseEvents.RightButtonDown)
                    //{
                    MouseLLHookStruct mouseHookStruct =
                    (MouseLLHookStruct)Marshal.PtrToStructure(StructParam, typeof(MouseLLHookStruct));
                    Point p = mouseHookStruct.pt;
                    Win32API.GetWindowRect(this.monitorInstance, out kvmRect);

                    bool widthInRange = p.X >= kvmRect.Left && p.X <= kvmRect.Right;
                    bool heightInRange = p.Y >= kvmRect.Top && p.Y <= kvmRect.Bottom;
                    if (widthInRange && heightInRange)
                    {
                        IntPtr formHandle = Win32API.WindowFromPoint(p);
                        if (IsContainsChildHandler(this.monitorInstance, formHandle))
                        {
                            if (mevent == WM_LBUTTONDOWN)
                            {
                                if (this.MouseDownEvent != null)
                                    MouseDownEvent(this, new MouseEventArgs(MouseButtons.Left, 1, p.X, p.Y, 0)); //鼠标左键事件
                            }
                            else if (mevent == WM_LBUTTONUP)
                            {
                                if (this.MouseUpEvent != null)
                                    MouseUpEvent(this, new MouseEventArgs(MouseButtons.Left, 1, p.X, p.Y, 0)); //鼠标左键事件
                            }
                            else if (mevent == WM_MOUSEMOVE)
                            {
                                if (this.MouseMoveEvent != null)
                                {
                                    MouseMoveEvent(this, new MouseEventArgs(MouseButtons.Left, 1, p.X, p.Y, 0));//鼠标移动
                                }
                            }
                            updateMouseTime();
                        }
                    }
                    //IntPtr formHandle = Win32API.WindowFromPoint(p);
                    // if(formHandle==this.monitorInstance)
                    // {
                    //      updateMouseTime();
                    // }
                    //} 
                }
                catch (Exception ex)
                {
                    //LogHelper.HostErrorInfo(string.Format("Hook MouseCallbackProcedure handler1--{0}", monitorInstance), ex);
                }
            }
            try
            {
                return Win32API.CallNextHookEx(_handleToHook, nCode, wparam, lparam);
            }
            catch (Exception ex)
            {
                //LogHelper.HostErrorInfo(string.Format("Hook MouseCallbackProcedure. handler--{0}", monitorInstance), ex);
                return 0;
            }

        }

        public MouseEventType GetEventType(Int32 wParam)
        {
            switch (wParam)
            {
                case WM_RBUTTONDCLICK:
                case WM_RBUTTONUCLICK:
                    return MouseEventType.DoubleClick;
                default:
                    return MouseEventType.None;
            }
        }
        [HandleProcessCorruptedStateExceptions]
        public void MouseHookStop()
        {
            if (_mouseStarted)
            {
                try
                {
                    Win32API.UnhookWindowsHookEx(_handleToHook);
                    _handleToHook = 0;
                    DoubleClick = null;
                    _mouseStarted = false;
                    this.MouseDownEvent = null;
                }
                catch (Exception ex)
                {
                    // LogHelper.HostErrorInfo(string.Format("Hook MouseHookStop. handler--{0}", monitorInstance), ex);
                }
            }
        }
        [HandleProcessCorruptedStateExceptions]
        public void KeyHookStop()
        {
            if (_keyStarted)
            {
                try
                {
                    Win32API.UnhookWindowsHookEx(_keyhandleToHook);
                    _keyhandleToHook = 0;
                    _keyStarted = false;
                }
                catch (Exception ex)
                {
                    // LogHelper.HostErrorInfo(string.Format("Hook KeyHookStop. handler--{0}", monitorInstance), ex);
                }

            }
        }

        public void Dispose()
        {
            Dispose(true);
        }
        [HandleProcessCorruptedStateExceptions]
        protected void Dispose(bool disposing)
        {
            if (!m_disposed)
            {
                try
                {
                    if (disposing)
                    {
                        GC.SuppressFinalize(this);
                    }
                    this.MouseHookStop();
                    this.KeyHookStop();
                    m_disposed = true;
                }
                catch (Exception ex)
                {
                    // LogHelper.HostErrorInfo(string.Format("Hook Dispose. handler--{0}", monitorInstance), ex);
                }

            }
        }

        ~Hook()
        {
            Dispose(false);
            //LogHelper.HostErrorInfo(string.Format("Hook Disposeing. Release Resource.--{0}", monitorInstance));
        }

        /// <summary>
        /// 判断指定父句柄 是否包含指定子句柄(查找3级)
        /// </summary>
        /// <param name="hWndChild"></param>
        /// <param name="hWndParent"></param>
        /// <param name="loopCount">查找级数</param>
        /// <returns></returns>
        private bool IsContainsChildHandler(IntPtr hWndParent,IntPtr hWndChild,int loopCount=10)
        {
            if (hWndChild == hWndParent)
                return true;
            for (int i = 0; i < loopCount; i++)
            {
                var parentHandle = Win32API.GetParent(hWndChild);
                if (parentHandle == hWndParent)
                    return true;
                if (parentHandle == IntPtr.Zero)
                    return false;
                hWndChild = parentHandle;
            }
            return false;
        }
    }
}